<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
 * CodeIgniter
 *
 * An open source application development framework for PHP 5.1.6 or newer
 *
 * @package		CodeIgniter
 * @author		ExpressionEngine Dev Team
 * @copyright	Copyright (c) 2008 - 2011, EllisLab, Inc.
 * @license		http://codeigniter.com/user_guide/license.html
 * @link		http://codeigniter.com
 * @since		Version 1.0
 * @filesource
 */

// ------------------------------------------------------------------------

/**
 * Session Class
 *
 * @package		CodeIgniter
 * @subpackage	Libraries
 * @category	Sessions
 * @author		MukherjeeInfoServices Dev Team
 * @link		http://codeigniter.com/user_guide/libraries/sessions.html
 */
class CI_Session {

	var $sess_use_database			= FALSE;
	var $sess_table_name			= '';
	var $sess_cookie_name			= 'ci_session';
	var $cookie_prefix				= '';
	var $time_reference				= 'time';
	var $flashdata_key				= 'flash';
	var $gc_probability				= 5;
	var $sess_expiration			= 7200;
	var $sess_match_ip				= FALSE;
	var $sess_match_useragent		= FALSE;
	var $sess_expire_on_close		= TRUE;
	var $cookie_path				= '';
	var $cookie_domain				= '';
	var $cookie_secure				= FALSE;
	var $CI;
	var $now;
	var $ua;

	/**
	 * Session Constructor
	 *
	 * The constructor runs the session routines automatically
	 * whenever the class is instantiated.
	 */
	public function __construct($params = array())
	{
		log_message('debug', "Session Class Initialized");

		// Set the super object to a local variable for use throughout the class
		$this->CI =& get_instance();

		// Set all the session preferences, which can either be set
		// manually via the $params array above or via the config file
		foreach (array('sess_use_database', 'sess_table_name', 'sess_cookie_name', 'cookie_prefix', 'sess_expiration', 'sess_expire_on_close', 'sess_match_ip', 'sess_match_useragent', 'time_reference', 'cookie_path', 'cookie_domain', 'cookie_secure') as $key)
		{
			$this->$key = (isset($params[$key])) ? $params[$key] : $this->CI->config->item($key);
		}

		// Are we using a database?  If so, load it
		if ($this->sess_use_database === TRUE AND $this->sess_table_name != '')
		{
			$this->CI->load->database();
		}

		// Set the "now" time.  Can either be GMT or server time, based on the
		// config prefs.  We use this to set the "last activity" time
		$this->now = $this->_get_time();
		
		$this->ua = substr($this->CI->input->user_agent(), 0, 120);
		
		// Set the cookie name
		$this->sess_cookie_name = $this->cookie_prefix.$this->sess_cookie_name;

		// Start the session
		$this->_session_start();
		

		// Delete 'old' flashdata (from last request)
		$this->_flashdata_sweep();

		// Mark all new flashdata as old (data will be deleted before next request)
		$this->_flashdata_mark();
		
		// Call the session garbage collection if needed
		$this->_sess_gc();

		log_message('debug', "Session routines successfully run");
	}
	// --------------------------------------------------------------------

	/**
	 * Destroy the current session
	 *
	 * @access	public
	 * @return	void
	 */
	function sess_destroy()
	{
		$this->_delete_database();
		session_destroy();
		$this->_session_start();
	}

	// --------------------------------------------------------------------

	/**
	 * Fetch a specific item from the session array
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */
	function userdata($item)
	{
		return ( ! isset($_SESSION[$item])) ? FALSE : $_SESSION[$item];
	}

	// --------------------------------------------------------------------

	/**
	 * Fetch all session data
	 *
	 * @access	public
	 * @return	array
	 */
	function all_userdata()
	{
		return $_SESSION;
	}

	// --------------------------------------------------------------------

	/**
	 * Add or change data in the "userdata" array
	 *
	 * @access	public
	 * @param	mixed
	 * @param	string
	 * @return	void
	 */
	function set_userdata($newdata = array(), $newval = '')
	{
		if (is_string($newdata))
		{
			$newdata = array($newdata => $newval);
		}

		if (count($newdata) > 0)
		{
			foreach ($newdata as $key => $val)
			{
				$_SESSION[$key] = $val;
			}
		}
		
		$this->_update_database();
	}

	// --------------------------------------------------------------------

	/**
	 * Delete a session variable from the "userdata" array
	 *
	 * @access	array
	 * @return	void
	 */
	function unset_userdata($newdata = array())
	{
		if (is_string($newdata))
		{
			$newdata = array($newdata => '');
		}

		if (count($newdata) > 0)
		{
			foreach ($newdata as $key => $val)
			{
				unset($_SESSION[$key]);
			}
		}
		$this->_update_database();
	}

	// ------------------------------------------------------------------------

	/**
	 * Add or change flashdata, only available
	 * until the next request
	 *
	 * @access	public
	 * @param	mixed
	 * @param	string
	 * @return	void
	 */
	function set_flashdata($newdata = array(), $newval = '')
	{
		if (is_string($newdata))
		{
			$newdata = array($newdata => $newval);
		}

		if (count($newdata) > 0)
		{
			foreach ($newdata as $key => $val)
			{
				$flashdata_key = $this->flashdata_key.':new:'.$key;
				$this->set_userdata($flashdata_key, $val);
			}
		}
	}

	// ------------------------------------------------------------------------

	/**
	 * Keeps existing flashdata available to next request.
	 *
	 * @access	public
	 * @param	string
	 * @return	void
	 */
	function keep_flashdata($key)
	{
		// 'old' flashdata gets removed.  Here we mark all
		// flashdata as 'new' to preserve it from _flashdata_sweep()
		// Note the function will return FALSE if the $key
		// provided cannot be found
		$old_flashdata_key = $this->flashdata_key.':old:'.$key;
		$value = $this->userdata($old_flashdata_key);

		$new_flashdata_key = $this->flashdata_key.':new:'.$key;
		$this->set_userdata($new_flashdata_key, $value);
	}

	// ------------------------------------------------------------------------

	/**
	 * Fetch a specific flashdata item from the session array
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */
	function flashdata($key)
	{
		$flashdata_key = $this->flashdata_key.':old:'.$key;
		return $this->userdata($flashdata_key);
	}

	// ------------------------------------------------------------------------

	/**
	 * Identifies flashdata as 'old' for removal
	 * when _flashdata_sweep() runs.
	 *
	 * @access	private
	 * @return	void
	 */
	function _flashdata_mark()
	{
		$userdata = $this->all_userdata();
		foreach ($userdata as $name => $value)
		{
			$parts = explode(':new:', $name);
			if (is_array($parts) && count($parts) === 2)
			{
				$new_name = $this->flashdata_key.':old:'.$parts[1];
				$this->set_userdata($new_name, $value);
				$this->unset_userdata($name);
			}
		}
	}

	// ------------------------------------------------------------------------

	/**
	 * Removes all flashdata marked as 'old'
	 *
	 * @access	private
	 * @return	void
	 */

	function _flashdata_sweep()
	{
		$userdata = $this->all_userdata();
		foreach ($userdata as $key => $value) 
		{
			if (strpos($key, ':old:'))
			{
				$this->unset_userdata($key);
			}
		}

	}

	// --------------------------------------------------------------------

	/**
	 * Get the "now" time
	 *
	 * @access	private
	 * @return	string
	 */
	function _get_time()
	{
		if (strtolower($this->time_reference) == 'gmt')
		{
			$now = time();
			$time = mktime(gmdate("H", $now), gmdate("i", $now), gmdate("s", $now), gmdate("m", $now), gmdate("d", $now), gmdate("Y", $now));
		}
		else
		{
			$time = time();
		}

		return $time;
	}

	// --------------------------------------------------------------------

	/**
	 * Serialize an array
	 *
	 * This function first converts any slashes found in the array to a temporary
	 * marker, so when it gets unserialized the slashes will be preserved
	 *
	 * @access	private
	 * @param	array
	 * @return	string
	 */
	function _serialize($data)
	{
		if (is_array($data))
		{
			foreach ($data as $key => $val)
			{
				if (is_string($val))
				{
					$data[$key] = str_replace('\\', '{{slash}}', $val);
				}
			}
		}
		else
		{
			if (is_string($data))
			{
				$data = str_replace('\\', '{{slash}}', $data);
			}
		}

		return serialize($data);
	}

	// --------------------------------------------------------------------

	/**
	 * Unserialize
	 *
	 * This function unserializes a data string, then converts any
	 * temporary slash markers back to actual slashes
	 *
	 * @access	private
	 * @param	array
	 * @return	string
	 */
	function _unserialize($data)
	{
		$data = @unserialize(strip_slashes($data));

		if (is_array($data))
		{
			foreach ($data as $key => $val)
			{
				if (is_string($val))
				{
					$data[$key] = str_replace('{{slash}}', '\\', $val);
				}
			}

			return $data;
		}

		return (is_string($data)) ? str_replace('{{slash}}', '\\', $data) : $data;
	}

	// --------------------------------------------------------------------

	/**
	 * Garbage collection
	 *
	 * This deletes expired session rows from database
	 * if the probability percentage is met
	 *
	 * @access	private
	 * @return	void
	 */
	private function _sess_gc()
	{
		if ($this->sess_use_database != TRUE)
		{
			return;
		}

		srand(time());
		if ((rand() % 100) < $this->gc_probability)
		{
			$expire = $this->now - $this->sess_expiration;

			$this->CI->db->where("last_activity < {$expire}");
			$this->CI->db->delete($this->sess_table_name);

			log_message('debug', 'Session garbage collection performed.');
		}
	}
	
	/**
	 * Session Create Database
	 * 
	 * Enter description here ...
	 * @return void
	 */
	private function _create_database()
	{
		if($this->sess_use_database === TRUE)
		{
			$this->CI->db->where('session_id', session_id());
			$res = $this->CI->db->get($this->sess_table_name);
			if($res->num_rows())
			{
				$info = $res->row();
				if($this->sess_match_ip === TRUE && ($this->now - $info->last_activity) > $this->sess_expiration)
					$this->sess_destroy();
				elseif($this->sess_match_useragent == TRUE && $this->ua != $info->user_agent)
					$this->sess_destroy();
			}
			else 
			{
				$this->CI->db->insert(
					$this->sess_table_name, 
					array(
						'session_id'	=> session_id(),
						'ip_address'	=> $this->CI->input->ip_address(),
						'user_agent'	=> $this->ua,
						'last_activity'	=> $this->now,
						'user_data'		=> $this->_serialize($_SESSION)
				));
			}
		}
	}
	
	/**
	 * Session Update Database
	 * 
	 * Enter description here ...
	 * @return void
	 */
	private function _update_database()
	{
		if($this->sess_use_database === TRUE)
		{
			$array = array(
				'ip_address'	=> $this->CI->input->ip_address(),
				'user_agent'	=> $this->ua,
				'last_activity'	=> $this->now,
				'user_data'		=> $this->_serialize($_SESSION)
			);
			$this->CI->db->where('session_id', session_id());
			$this->CI->db->update($this->sess_table_name, $array);
		}
	}
	
	private function _delete_database($id = null)
	{
		if($this->sess_use_database === TRUE)
		{
			if(is_null($id)) $id = session_id();
			$this->CI->db->where('session_id', $id);
			$this->CI->db->delete($this->sess_table_name);
		}
	}
	
	/**
	 * Session Start
	 * 
	 * Enter description here ...
	 * @return void
	 */
	private function _session_start()
	{
		// Run the Session routine. If a session doesn't exist we'll
		// create a new one.  If it does, we'll update it.
		@session_name($this->sess_cookie_name);
		@session_set_cookie_params(
			(($this->sess_expire_on_close === TRUE) ? 0 : $this->sess_expiration),
			$this->cookie_path,
			$this->cookie_domain,
			$this->cookie_secure
			);
		if($this->CI->input->cookie($this->sess_cookie_name) === FALSE) 
		{
			$new_sessid = '';
			while (strlen($new_sessid) < 32)
				$new_sessid .= mt_rand(0, mt_getrandmax());
	
			// To make the session ID even more secure we'll combine it with the user's IP
			$new_sessid .= $this->CI->input->ip_address(); 
			
			// Turn it into a hash
			$new_sessid = md5(uniqid($new_sessid, TRUE));
			@session_id($new_sessid);
		}
		@session_start();
		$_SESSION['session_id']		= session_id();
		//$_SESSION['ip_address']		= $this->CI->input->ip_address();
		$_SESSION['user_agent']		= $this->ua;
		$_SESSION['last_activity']	= $this->now;
		$this->_create_database();
	}
}
// END Session Class

/* End of file Session.php */
/* Location: ./application/libraries/Session.php */